package com.dj.mingkong.admin.biz.service.system.impl;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.dj.mingkong.admin.biz.controller.system.vo.request.LoginVo;
import com.dj.mingkong.admin.biz.controller.system.vo.request.UserQueryVo;
import com.dj.mingkong.admin.biz.controller.system.vo.request.UserUpdateVo;
import com.dj.mingkong.admin.biz.controller.system.vo.response.UserInfoVo;
import com.dj.mingkong.admin.biz.controller.system.vo.response.UserVo;
import com.dj.mingkong.admin.biz.repository.database.system.RoleRepository;
import com.dj.mingkong.admin.biz.repository.database.system.UserLoginLogRepository;
import com.dj.mingkong.admin.biz.repository.database.system.UserRepository;
import com.dj.mingkong.admin.biz.repository.database.system.UserRoleRepository;
import com.dj.mingkong.admin.biz.service.system.UserService;
import com.dj.mingkong.admin.biz.util.JwtUtil;
import com.dj.mingkong.common.constant.RedisConstants;
import com.dj.mingkong.common.enums.LoginLogAppCodeEnum;
import com.dj.mingkong.common.enums.LoginLogTypeEnum;
import com.dj.mingkong.common.enums.RoleEnum;
import com.dj.mingkong.common.enums.StatusEnum;
import com.dj.mingkong.common.exception.ValidateException;
import com.dj.mingkong.common.infrastructure.base.dto.context.UserInfoContext;
import com.dj.mingkong.common.infrastructure.base.dto.delete.DeleteVo;
import com.dj.mingkong.common.infrastructure.base.dto.page.PageVo;
import com.dj.mingkong.common.infrastructure.eo.database.system.RoleEo;
import com.dj.mingkong.common.infrastructure.eo.database.system.UserEo;
import com.dj.mingkong.common.infrastructure.eo.database.system.UserLoginLogEo;
import com.dj.mingkong.common.infrastructure.eo.database.system.UserRoleEo;
import com.dj.mingkong.common.microservice.UserContext;
import com.dj.mingkong.common.util.AESUtils;
import com.dj.mingkong.common.util.AssertUtils;
import com.dj.mingkong.common.util.IpUtils;
import com.dj.mingkong.common.util.SmartBeanCopy;
import com.google.code.kaptcha.Producer;
import jakarta.annotation.Resource;
import jakarta.servlet.ServletOutputStream;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.time.LocalDateTime;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * 用户服务实现类
 *
 * @author zenglonghui
 * @date: 2022/5/25 18:09
 */
@Slf4j
@Service("userService")
public class UserServiceImpl implements UserService {
    @Resource
    private UserRepository userRepository;
    @Resource
    private RedisTemplate<String, String> redisTemplate;
    @Resource
    private UserRoleRepository userRoleRepository;
    @Resource
    private RoleRepository roleRepository;
    @Resource
    private JwtUtil jwtUtil;
    @Resource
    private Producer verifyCodeProducer;
    @Resource
    private UserLoginLogRepository userLoginLogRepository;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public String login(HttpServletRequest request, LoginVo loginVo) {
        var userEo = userRepository.oneByUsername(loginVo.getUsername());
        //校验账号密码
        checkUser(userEo, loginVo);
        //生成token
        final String token = createToken(userEo);
        redisTemplate.boundValueOps(RedisConstants.TOKEN + userEo.getUsername())
                .set(token, RedisConstants.EXPIRE_TIME, TimeUnit.MINUTES);
        saveLoginLog(request, userEo);
        return token;
    }

    private void saveLoginLog(HttpServletRequest request, UserEo userEo) {
        final UserLoginLogEo userLoginLogEo = UserLoginLogEo.builder()
                .loginType(LoginLogTypeEnum.ADMIN.getType()).userId(userEo.getId())
                .appCode(LoginLogAppCodeEnum.PC.getCode()).username(userEo.getUsername())
                .loginTime(LocalDateTime.now()).loginIp(IpUtils.getIpAddress(request)).build();
        userLoginLogRepository.save(userLoginLogEo);
    }

    @Override
    public void getVerifyCodeImage(HttpServletRequest request, HttpServletResponse response) {
        try (ServletOutputStream out = response.getOutputStream()) {
            final String key = request.getParameter("key");
            response.setDateHeader("Expires", 0);
            response.setHeader("Cache-Control", "no-store, no-cache, must-revalidate");
            response.addHeader("Cache-Control", "post-check=0, pre-check=0");
            response.setHeader("Pragma", "no-cache");
            response.setContentType("image/jpeg");
            //生成验证码
            final String capText = verifyCodeProducer.createText();
            log.info("生成验证码key:{}, capText:{}", key, capText);
            redisTemplate.boundValueOps(RedisConstants.VERIFY_CODE + key)
                    .set(capText, RedisConstants.VERIFY_CODE_EXPIRE_TIME, TimeUnit.MINUTES);
            //向客户端写出
            final BufferedImage image = verifyCodeProducer.createImage(capText);
            ImageIO.write(image, "jpg", out);
            out.flush();
        } catch (Exception e) {
            log.error("getVerifyCodeImage error", e);
        }
    }

    @Override
    public UserInfoVo info() {
        final Long userId = UserContext.getUserId();
        final UserEo userEo = userRepository.getById(userId);
        AssertUtils.isNotNull(userEo, "userEo is null");
        final UserInfoVo userInfoVo = SmartBeanCopy.copy(userEo, UserInfoVo.class);
        userInfoVo.setRoles(getUserRoles(userId));
        return userInfoVo;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void updateById(UserUpdateVo updateVo) {
        final UserEo userEo = SmartBeanCopy.copy(updateVo, UserEo.class);
        userRepository.updateById(userEo);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void deleteById(DeleteVo deleteVo) {
        userRepository.deleteById(deleteVo.getId());
    }

    @Override
    public PageVo<UserVo> pageByVo(UserQueryVo queryVo) {
        final Page<UserEo> userEoPage = userRepository.listPageByVo(queryVo);
        return SmartBeanCopy.copyPageVo(userEoPage, UserVo.class);

    }


    private List<String> getUserRoles(Long userId) {
        final List<UserRoleEo> userRoleEos = userRoleRepository.listByUserId(userId);
        if (userRoleEos.isEmpty()) {
            return Collections.emptyList();
        }
        final List<Long> roleIds = userRoleEos.stream().map(UserRoleEo::getRoleId).toList();
        final List<RoleEo> roleEos = roleRepository.listByIds(roleIds);
        return roleEos.stream().filter(roleEo -> StatusEnum.VALID.equalsIntVal(roleEo.getStatus()))
                .map(RoleEo::getCode).toList();
    }

    private String createToken(UserEo userEo) {
        final UserInfoContext userInfoContext = SmartBeanCopy.copy(userEo, UserInfoContext.class);
        final String userString = JSON.toJSONString(userInfoContext);
        log.info("把用户信息转成字符串:{}: ", userString);
        //获取角色
        final List<String> roleList = getUserRoles(userEo.getId());
        final String roles = JSON.toJSONString(roleList);
        log.info("把角色集合对象转成json:{}: ", roles);
        //登录成功,签发token   参数1:用户登陆名  参数2:用户实体类  参数3:角色
        return jwtUtil.createJwt(userInfoContext.getUsername(), userString, roles);
    }

    private void checkUser(UserEo userEo, LoginVo loginVo) {
        AssertUtils.isNotNull(userEo, "账号不存在！");
        if (StatusEnum.INVALID.equalsIntVal(userEo.getStatus())) {
            throw new ValidateException("账号已冻结，请联系管理员！");
        }
        if (!loginVo.getPassword().equals(AESUtils.decrypt(userEo.getPassword(), ""))) {
            throw new ValidateException("账号或密码错误！");
        }
        final List<String> roles = getUserRoles(userEo.getId());
        if (CollectionUtils.isEmpty(roles)) {
            throw new ValidateException("您无权限登录，请联系管理员！");
        }
        if (!roles.contains(RoleEnum.ADMIN.getCode())
                && !roles.contains(RoleEnum.AUDITOR.getCode())) {
            throw new ValidateException("您无权限登录，请联系管理员！");
        }
    }

}
